package com.boot.common;

import org.springframework.beans.factory.annotation.Value;

import java.io.Serializable;
import java.util.Date;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Message.RecipientType;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;


class MailUtil implements Serializable {
    private static final long serialVersionUID = 1L;
    /** 发件人账号 */
    @Value("${spring.mail.host}")
    private String account;
    /** 发件人名称 */
    private String name;
    /** 发件人密码或授权码 */
    private String password;
    /** SMTP服务器地址 */
    private String host;
    /** 邮件编码 */
    private String encoding;

    /**
     * 创建一个编码为UTF-8的邮件发送器
     *
     * @param account
     *            用来发送邮件的账号
     * @param name
     *            用来发送邮件的账号的名称
     * @param password
     *            账号对应的密码<br>
     *            如果该邮箱服务商规定第三方需要使用授权码,则此处应填写授权码
     * @param host
     *            邮箱的smtp服务器地址<br>
     *            一般格式是smtp.xxx.com,例如网易163的是:smtp.163.com<br>
     *            <br>
     *            注:需手动开启POP3/SMTP/IMAP服务
     */
    public MailUtil(String account, String name, String password, String host) {
        this.account = account;
        this.name = name;
        this.password = password;
        this.host = host;
        this.encoding = "UTF-8";
    }

    /**
     * 根据内容获取MimeBodyPart
     *
     * @param content
     *            文本内容,如果引用本地文件请使用src='cid:image_fairy_tail'的形式<br>
     *            网络文件则忽略images参数
     * @param isCode
     *            是否为html代码<br>
     *            如果为false,则发送纯文本,且files参数无效
     * @param files
     *            本地图片加载,数组第一个元素为文件全路径,第二个元素为cid名称,cid是content的引用
     *
     * @return 单个part节点
     * @throws Exception
     */
    public MimeBodyPart getPart(String content, Boolean isCode, String[]... files) throws Exception {
        MimeBodyPart part = new MimeBodyPart();
        if (!isCode) {// 如果为false,则发送纯文本
            part.setContent(content, "text/plain;charset=" + this.encoding);
            return part;
        }
        // 新建一个大节点
        MimeMultipart mimeMultipart = new MimeMultipart();

        // 解析images生成part
        for (String[] file : files) {
            // 新建文件节点
            MimeBodyPart filePart = new MimeBodyPart();
            // 读入本地文件
            DataHandler data = new DataHandler(new FileDataSource(file[0]));
            // 将文件数据添加到文件节点
            filePart.setDataHandler(data);
            // 为文件节点设置一个唯一编号(在content将引用该ID)
            filePart.setContentID(file[1]);
            // 将该文件添加至大节点
            mimeMultipart.addBodyPart(filePart);
        }

        // 新建文本节点
        MimeBodyPart textPart = new MimeBodyPart();
        // 将文本加入至节点
        textPart.setContent(content, "text/html;charset=" + this.encoding);
        // 将节点整合到大节点
        mimeMultipart.addBodyPart(textPart);
        // 设置节点关系为关联
        mimeMultipart.setSubType("related");
        // 将大节点封装成节点
        part.setContent(mimeMultipart);
        return part;
    }

    /**
     * 获取单个附件节点
     *
     * @param file
     *            附件全路径
     * @param name
     *            附件名,传入null则使用file的名称
     * @return 单个part节点
     * @throws Exception
     */
    public MimeBodyPart getPart(String file, String name) throws Exception {
        // 创建附件节点
        MimeBodyPart part = new MimeBodyPart();
        // 读取本地文件
        DataHandler data = new DataHandler(new FileDataSource(file));
        // 将附件数据添加到附件节点
        part.setDataHandler(data);
        // 设置附件名称(需要编码)
        if (name == null || "".equals(name)) {// 如果附件名为null或"",则使用file文件名
            part.setFileName(MimeUtility.encodeText(data.getName()));
            return part;
        }
        part.setFileName(MimeUtility.encodeText(name));
        return part;
    }

    /**
     * 得到收件人信息
     *
     * @param receiver
     *            收件人地址
     * @return InternetAddress[]
     * @throws Exception
     */
    public InternetAddress[] getReceivers(String... receiver) throws Exception {
        if (receiver.length < 0) {
            throw new RuntimeException("收件人为空");
        }
        // 将字符串数组转为收件人数组
        InternetAddress[] receiverArray = new InternetAddress[receiver.length];
        for (int i = 0; i < receiverArray.length; i++) {
            receiverArray[i] = new InternetAddress(receiver[i]);
        }
        return receiverArray;
    }

    /**
     * 发送邮件
     *
     * @param receivers
     *            收件人数组,使用getReceivers()方法获得
     * @param subject
     *            邮件主题
     * @param recipientType
     *            发送类型<br>
     *            普通:RecipientType.TO<br>
     *            抄送:RecipientType.CC<br>
     *            密送:RecipientType.BCC
     * @param parts
     *            节点,使用getPart()方法获得
     * @throws Exception
     */
    public void send(InternetAddress[] receivers, String subject, RecipientType recipientType, MimeBodyPart... parts)
            throws Exception {


        // 创建会话参数
        Properties props = new Properties();
        // 设置使用的协议(JavaMail规范要求)
        props.setProperty("mail.transport.protocol", "smtp");
        // 设置发件人的邮箱的SMTP服务器地址
        props.setProperty("mail.smtp.host", this.host);
        // 设置需要请求认证
        props.setProperty("mail.smtp.auth", "true");

        // 根据会话参数创建会话对象, 用于和邮件服务器交互
        Session session = Session.getInstance(props);
        // 设置为debug模式,可以查看详细的发送 log
        session.setDebug(true);

        // 设置发件人信息
        InternetAddress sender = new InternetAddress(this.account, this.name, this.encoding);

        // 创建一封邮件
        MimeMessage message = createMimeMessage(session, subject, RecipientType.TO, sender, receivers, parts);

        // 保存到本地查看
        // message.writeTo(FileOutputStream);

        // 根据Session获取邮件传输对象
        Transport transport = session.getTransport();
        // 使用邮箱账号和密码连接邮件服务器
        // 这里认证的邮箱必须与message中的发件人邮箱一致,否则报错
        transport.connect(this.account, this.password);
        // 发送邮件,发到所有的收件地址,message.getAllRecipients()获取到的是在创建邮件对象时添加发送类型
        transport.sendMessage(message, message.getAllRecipients());
        // 关闭连接
        transport.close();
    }

    /**
     * 创建一封邮件
     *
     * @param session
     *            用于和邮件服务器交互的会话
     * @param subject
     *            邮件主题
     * @param recipientType
     *            发送类型:<br>
     *            普通:RecipientType.TO<br>
     *            抄送:RecipientType.CC<br>
     *            密送:RecipientType.BCC
     * @param sender
     *            发件人邮箱
     * @param receivers
     *            收件人邮箱数组
     * @param parts
     *            邮件内容节点
     * @return 创建好的邮件对象
     * @throws Exception
     */
    private MimeMessage createMimeMessage(Session session, String subject, RecipientType recipientType,
                                          InternetAddress sender, InternetAddress[] receivers, MimeBodyPart... parts) throws Exception {
        if (receivers.length < 0) {
            throw new RuntimeException("收件人为空");
        }
        // 创建邮件对象
        MimeMessage message = new MimeMessage(session);
        // 设置发件人
        message.setFrom(sender);
        // 设置收件人
        for (InternetAddress receiver : receivers) {
            message.addRecipient(recipientType, receiver);
        }
        // 设置邮件主题
        message.setSubject(subject, this.encoding);

        // 创建大节点
        MimeMultipart multipart = new MimeMultipart();
        // 将传入的内容节点依次添加进大节点
        for (MimeBodyPart part : parts) {
            multipart.addBodyPart(part);
        }
        // 设置节点关系,不设置则为混合:mixed,可以设置为关联:related
        multipart.setSubType("mixed");
        // 设置邮件内容
        message.setContent(multipart);

        // 设置发件时间
        message.setSentDate(new Date());

        // 保存所有设置
        message.saveChanges();
        return message;
    }

    

    public static void main(String[] args) throws Exception {
        MailUtil mailTransmitter = new MailUtil("819546823@qq.com", "ds", "ugbnesgyrmrkbcig","smtp.qq.com");
        // 发送一封普通邮件
        // 设置内容
//         MimeBodyPart part = mailTransmitter.getPart("这是一封普通邮件<br />html标签无效", false);
        // 设置收件人
        InternetAddress[] receivers = mailTransmitter.getReceivers("819546823@qq.com","ds_workEmail@163.com");
        // 发送
//         mailTransmitter.send(receivers, "邮件测试", RecipientType.TO, part);

        // 发送一封带图片的邮件
        // 设置图片,id尽量不要用数字,测试时image1和image2报554 DT:SPM错误,即网易垃圾邮件判定
         String[][] images = { { "D:/1.jpg", "my_image" }, { "D:/1.jpg", "my_img" } };
        // 设置内容
        // 此处删除body和前面标签会报554 DT:SPM错误
         MimeBodyPart part = mailTransmitter.getPart(
        		"<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title></title></head><body>这是一封超文本邮件<br />html标签有效<img src='cid:my_image' /><img src='cid:my_img' /><img src='https://www.java.com/feo-cdn/r/_/YWcerH870.jpg' /></body></html>",
        		true, images);
        System.out.println(part);
        // 群发带附件的邮件,测试密送
        // 设置内容
//        MimeBodyPart part = mailTransmitter.getPart("这是一封带2个附件的邮件<br />请主意查收", true);
        // 发送
        mailTransmitter.send(receivers, "邮件测试", RecipientType.BCC, part,mailTransmitter.getPart("D:/1.jpg", "图片1"),mailTransmitter.getPart("D:/1.jpg", "图片2"));
    }
}